package com.ssy.lingxi.order.serviceimpl.feign;

import cn.hutool.core.util.StrUtil;
import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.constant.basic.EnableDisableStatus;
import com.ssy.lingxi.common.constant.basic.FundModeEnum;
import com.ssy.lingxi.common.constant.basic.UserLoginSourceEnum;
import com.ssy.lingxi.common.constant.manage.ShopEnvironmentEnum;
import com.ssy.lingxi.common.constant.order.OrderPayChannelEnum;
import com.ssy.lingxi.common.constant.order.OrderPaymentParameterEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.component.redis.service.IRedisUtils;
import com.ssy.lingxi.order.model.bo.PaymentParameterBO;
import com.ssy.lingxi.order.model.constant.OrderPaymentCallbackStatusEnum;
import com.ssy.lingxi.order.model.dto.PayAmountDTO;
import com.ssy.lingxi.order.service.feign.IPayFeignService;
import com.ssy.lingxi.order.utils.OrderStringUtil;
import com.ssy.lingxi.pay.api.common.CcbB2bPayResultEnum;
import com.ssy.lingxi.pay.api.common.EAccountPayChannelEnum;
import com.ssy.lingxi.pay.api.common.PayChannelEnum;
import com.ssy.lingxi.pay.api.common.ServiceTypeEnum;
import com.ssy.lingxi.pay.api.dto.request.MobilePayRequest;
import com.ssy.lingxi.pay.api.dto.request.WeChatRefundRequest;
import com.ssy.lingxi.pay.api.dto.request.aliPay.AliPayComputerRequest;
import com.ssy.lingxi.pay.api.dto.request.aliPay.AliPayRefundRequest;
import com.ssy.lingxi.pay.api.dto.request.assetAccount.BalancePayRequest;
import com.ssy.lingxi.pay.api.dto.request.assetAccount.BalanceRefundRequest;
import com.ssy.lingxi.pay.api.dto.request.eAccount.CacheParamsRequest;
import com.ssy.lingxi.pay.api.dto.request.eAccount.EAccountOrderPayRequest;
import com.ssy.lingxi.pay.api.dto.request.eAccount.EAccountRefundRequest;
import com.ssy.lingxi.pay.api.dto.request.eAccount.ReceiveRequest;
import com.ssy.lingxi.pay.api.feign.*;
import com.ssy.lingxi.pay.api.model.vo.request.CreditFeignVO;
import com.ssy.lingxi.pay.api.model.vo.request.CreditInitVO;
import com.ssy.lingxi.pay.api.model.vo.request.CreditPayVO;
import com.ssy.lingxi.pay.api.model.vo.request.CreditRefundVO;
import com.ssy.lingxi.pay.api.model.vo.request.ccb.*;
import com.ssy.lingxi.pay.api.model.vo.response.CreditFeignDetailVO;
import com.ssy.lingxi.pay.api.model.vo.response.CreditPayResponseVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 支付服务OpenFeign接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2021-08-18
 */
@Service
public class PayFeignServiceImpl implements IPayFeignService {
    private static final Logger logger = LoggerFactory.getLogger(PayFeignServiceImpl.class);

    @Resource
    private IWeChatPayFeign weChatPayFeign;

    @Resource
    private IAliPayFeign aliPayFeign;

    @Resource
    private IAssetAccountFeign assetAccountFeign;

    @Resource
    private ICreditControllerFeign creditControllerFeign;

    @Resource
    private IAllInPayFeign allInPayFeign;

    @Resource
    private ICcbPayFeign ccbPayFeign;

    @Resource
    private IRedisUtils redisUtils;

    /**
     * 缓存通联支付参数
     *
     * @param parameters 支付参数
     */
    @Async
    @Override
    public void cacheAllInPayParameters(List<PaymentParameterBO> parameters) {
        if(CollectionUtils.isEmpty(parameters)) {
            return;
        }

        List<PaymentParameterBO> cacheList = new ArrayList<>();
        for (OrderPaymentParameterEnum allInPayParameter : OrderPaymentParameterEnum.findAllInPayParameters()) {
            PaymentParameterBO parameterBO = parameters.stream().filter(p -> p.getCode().equals(allInPayParameter.getCode()) && StringUtils.hasLength(p.getValue())).findFirst().orElse(null);
            if(parameterBO != null && cacheList.stream().noneMatch(cache -> cache.getCode().equals(parameterBO.getCode()))) {
                PaymentParameterBO p = new PaymentParameterBO();
                p.setCode(parameterBO.getCode());
                p.setValue(parameterBO.getValue());
                cacheList.add(p);
            }
        }

        if(CollectionUtils.isEmpty(cacheList)) {
            return;
        }

        List<CacheParamsRequest> cacheParamsRequest = cacheList.stream().map(parameter -> {
            CacheParamsRequest request = new CacheParamsRequest();
            request.setCode(parameter.getCode());
            request.setValue(parameter.getValue());
            return request;
        }).collect(Collectors.toList());
        try {
            Wrapper<Void> cacheResult = allInPayFeign.cacheParams(cacheParamsRequest);
            if(cacheResult.getCode() != ResponseCode.SUCCESS.getCode()) {
                logger.error("缓存通联支付参数错误，code:" + cacheResult.getCode() + ", msg:" + cacheResult.getMessage());
            }
        } catch (Exception e) {
            logger.error("缓存通联支付参数错误, msg:" + e.getMessage());
        }
    }

    /**
     * 删除建行支付参数
     */
    @Override
    public void removeCcbPayParameters() {
        redisUtils.del(Constants.REDIS_KEY_CCB_PAY_AUTH, Constants.REDIS_PAY_INDEX);
    }

    /**
     * 微信Web Native支付
     *
     * @param loginSource       用户登录来源（用于标识Web、App支付）
     * @param recipientMemberId 收款方会员Id
     * @param recipientRoleId   收款方会员角色Id
     * @param fundMode          资金归集模式
     * @param tradeNo           生成的微信订单号
     * @param digest            订单摘要
     * @param payAmount         支付金额
     * @param attach            透传参数
     * @param merchantId        微信商户号
     * @param appId             微信AppId
     * @param apiKey            微信ApiKey
     * @param shopEnvironment   商城适用环境  ShopEnvironmentEnum.class
     * @return 二维码链接
     */
    @Override
    public Wrapper<String> weChatPay(Integer loginSource, Long recipientMemberId, Long recipientRoleId, Integer fundMode, String tradeNo, String digest, BigDecimal payAmount, String attach, String merchantId, String appId, String apiKey, Integer shopEnvironment) {
        MobilePayRequest payRequest = new MobilePayRequest();
        payRequest.setMemberId(recipientMemberId);
        payRequest.setMemberRoleId(recipientRoleId);
        //微信订单号
        payRequest.setOrderCode(tradeNo);
        //透传参数
        payRequest.setAttach(attach);
        payRequest.setPayMoney(payAmount.setScale(2, RoundingMode.HALF_UP).doubleValue());
        payRequest.setServiceType(ServiceTypeEnum.Order_Pay.getCode());
        payRequest.setRemark(StringUtils.hasLength(digest) ? digest : "");
        payRequest.setPayType(fundMode.equals(FundModeEnum.PLATFORM_EXCHANGE.getCode()) ? 1 : 2);
        payRequest.setPayChannel(loginSource.equals(UserLoginSourceEnum.BUSINESS_WEB.getCode()) || loginSource.equals(UserLoginSourceEnum.BUSINESS_MANAGEMENT_PLATFORM.getCode()) ? 1 : 2);
        if(ShopEnvironmentEnum.H5.getCode().equals(shopEnvironment)){
            payRequest.setPayChannel(PayChannelEnum.JsApi.getCode());
        }
        payRequest.setMerchantId(merchantId);
        payRequest.setAppId(appId);
        payRequest.setApiKey(apiKey);
        return weChatPayFeign.orderPay(payRequest);
    }

    /**
     * 微信小程序支付
     *
     * @param recipientMemberId 收款方会员Id
     * @param recipientRoleId   收款方会员角色Id
     * @param fundMode          资金归集模式
     * @param tradeNo           订单号
     * @param digest            订单摘要
     * @param payAmount         支付金额
     * @param attach            透传参数
     * @param merchantId        微信商户号
     * @param apiKey            微信ApiKey
     * @param appletAppId       微信AppId
     * @param appletAppKey      微信开发者密钥AppKey
     * @param weChatCode        小程序登录凭证
     * @return 二维码链接
     */
    @Override
    public Wrapper<String> weChatMiniAppPay(Long recipientMemberId, Long recipientRoleId, Integer fundMode, String tradeNo, String digest, BigDecimal payAmount, String attach, String merchantId, String apiKey, String appletAppId, String appletAppKey, String weChatCode) {
        MobilePayRequest payRequest = new MobilePayRequest();
        payRequest.setMemberId(recipientMemberId);
        payRequest.setMemberRoleId(recipientRoleId);
        //微信订单号
        payRequest.setOrderCode(tradeNo);
        //透传参数
        payRequest.setAttach(attach);
        payRequest.setPayMoney(payAmount.setScale(2, RoundingMode.HALF_UP).doubleValue());
        payRequest.setServiceType(ServiceTypeEnum.Order_Pay.getCode());
        payRequest.setRemark(StringUtils.hasLength(digest) ? digest : "");
        payRequest.setPayType(fundMode.equals(FundModeEnum.PLATFORM_EXCHANGE.getCode()) ? 1 : 2);
        payRequest.setPayChannel(3);
        payRequest.setMerchantId(merchantId);
        payRequest.setApiKey(apiKey);
        payRequest.setAppletAppId(appletAppId);
        payRequest.setAppletAppKey(appletAppKey);
        payRequest.setJsCode(weChatCode);
        return weChatPayFeign.orderPay(payRequest);
    }

    /**
     * 微信退款
     *
     * @param recipientMemberId 收款方会员Id
     * @param recipientRoleId   收款方会员角色Id
     * @param fundMode          资金归集模式
     * @param tradeNo           支付单号
     * @param payAmount         订单支付金额
     * @param refundNo          退款单号
     * @param refundAmount      退款金额
     * @param merchantId        微信商户Id
     * @param appId             微信AppId
     * @param apiKey            微信ApiKey
     * @param keyPath           微信退款证书路径
     * @return 退款结果
     */
    @Override
    public Wrapper<Void> weChatRefund(Long recipientMemberId, Long recipientRoleId, Integer fundMode, String tradeNo, BigDecimal payAmount, String refundNo, BigDecimal refundAmount, String merchantId, String appId, String apiKey, String keyPath) {
        WeChatRefundRequest request = new WeChatRefundRequest();
        request.setMemberId(recipientMemberId);
        request.setMemberRoleId(recipientRoleId);
        request.setOutTradeNo(tradeNo);
        request.setPayMoney(payAmount.doubleValue());
        request.setOutRefundNo(refundNo);
        request.setRefundMoney(refundAmount.doubleValue());
        request.setPayType(fundMode.equals(FundModeEnum.PLATFORM_EXCHANGE.getCode()) ? 1 : 2);
        request.setMerchantId(merchantId);
        request.setAppId(appId);
        request.setApiKey(apiKey);
        request.setKeyPath(keyPath);

        try {
            logger.info("微信支付退款参数 => " + OrderStringUtil.serializeObject(request));
            Wrapper<?> refundResult = weChatPayFeign.refund(request);
            logger.error("微信退款返回 => code:" + refundResult.getCode() + ", msg:" + refundResult.getMessage());

            if(refundResult.getCode() != ResponseCode.SUCCESS.getCode()) {
                return Wrapper.fail(refundResult.getCode(), refundResult.getMessage());
            }

            return Wrapper.success();
        } catch (Exception e) {
            logger.error("微信退款异常 => msg:" + e.getMessage());
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        }
    }

    /**
     * 微信Web Native支付
     *
     * @param loginSource       用户登录来源（用于标识Web、App支付）
     * @param recipientMemberId 收款方会员Id
     * @param recipientRoleId   收款方会员角色Id
     * @param fundMode          资金归集模式
     * @param tradeNo           订单号
     * @param digest            订单摘要
     * @param payAmount         支付金额
     * @param attach            透传参数
     * @param appId             支付宝AppId
     * @param publicKey         支付宝publicKey
     * @param privateKey        支付宝privateKey
     * @param appAuthToken      支付宝第三方授权Token
     * @param shopEnvironment   商城适用环境  ShopEnvironmentEnum.class
     * @return 二维码链接
     */
    @Override
    public Wrapper<String> aliPay(Integer loginSource, Long recipientMemberId, Long recipientRoleId, Integer fundMode, String tradeNo, String digest, BigDecimal payAmount, String attach, String appId, String publicKey, String privateKey, String appAuthToken,Integer shopEnvironment) {
        AliPayComputerRequest payRequest = new AliPayComputerRequest();
        payRequest.setMemberId(recipientMemberId);
        payRequest.setMemberRoleId(recipientRoleId);
        //支付宝订单号
        payRequest.setOrderCode(tradeNo);
        //透传参数
        payRequest.setBody(attach);
        payRequest.setPayMoney(payAmount.setScale(2, RoundingMode.HALF_UP).doubleValue());
        payRequest.setServiceType(ServiceTypeEnum.Order_Pay.getCode());
        payRequest.setRemark(StringUtils.hasLength(digest) ? digest : "");
        payRequest.setPayType(fundMode.equals(FundModeEnum.PLATFORM_EXCHANGE.getCode()) ? 1 : 2);
        //根据登录来源转换支付渠道
        payRequest.setPayChannel(loginSource.equals(UserLoginSourceEnum.BUSINESS_WEB.getCode()) || loginSource.equals(UserLoginSourceEnum.BUSINESS_MANAGEMENT_PLATFORM.getCode()) ? 1 : 2);
        if(ShopEnvironmentEnum.H5.getCode().equals(shopEnvironment)){
            payRequest.setPayChannel(PayChannelEnum.JsApi.getCode());
        }
        payRequest.setAppId(appId);
        payRequest.setAlipayPublicKey(publicKey);
        payRequest.setAppPrivateKey(privateKey);
        payRequest.setAppAuthToken(appAuthToken);
        return aliPayFeign.orderPay(payRequest);
    }

    /**
     * 支付宝退款
     *
     * @param vendorMemberId 供应会员Id
     * @param vendorRoleId   供应会员角色id
     * @param fundMode       资金归集模式
     * @param tradeNo        交易单号
     * @param refundNo       退款单号
     * @param refundAmount   退款金额
     * @param appId          支付宝AppId
     * @param publicKey      支付宝公钥
     * @param privateKey     支付宝私钥
     * @param appAuthToken   支付宝第三方授权Token
     * @return 退款结果
     */
    @Override
    public Wrapper<Void> aliPayRefund(Long vendorMemberId, Long vendorRoleId, Integer fundMode, String tradeNo, String refundNo, BigDecimal refundAmount, String appId, String publicKey, String privateKey, String appAuthToken) {
        AliPayRefundRequest request = new AliPayRefundRequest();
        request.setMemberId(vendorMemberId);
        request.setMemberRoleId(vendorRoleId);
        request.setTransactionId(tradeNo);
        request.setOutRefundNo(refundNo);
        request.setRefundMoney(refundAmount.doubleValue());
        request.setRefundReason("");
        request.setPayType(fundMode.equals(FundModeEnum.PLATFORM_EXCHANGE.getCode()) ? 1 : 2);
        request.setAppId(appId);
        request.setPublicKey(publicKey);
        request.setPrivateKey(privateKey);
        request.setAppAuthToken(appAuthToken);

        try {
            Wrapper<?> refundResult = aliPayFeign.refund(request);
            if(refundResult.getCode() != ResponseCode.SUCCESS.getCode()) {
                return Wrapper.fail(refundResult.getCode(), refundResult.getMessage());
            }

            return Wrapper.success();
        } catch (Exception e) {
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        }
    }

    /**
     * 余额支付
     *
     * @param buyerMemberId     采购会员Id
     * @param buyerRoleId       采购会员角色Id
     * @param recipientMemberId 收款方会员Id
     * @param recipientRoleId   收款方会员角色Id
     * @param fundMode          资金归集模式
     * @param orderNo           订单号
     * @param payAmount         支付金额
     * @param payPassword       Aes加密后的支付密码
     * @return 支付结果
     */
    @Override
    public Wrapper<String> balancePay(Long buyerMemberId, Long buyerRoleId, Long recipientMemberId, Long recipientRoleId, Integer fundMode, String orderNo, BigDecimal payAmount, String payPassword) {
        BalancePayRequest request = new BalancePayRequest();
        request.setMemberId(buyerMemberId);
        request.setMemberRoleId(buyerRoleId);
        request.setParentMemberId(recipientMemberId);
        request.setParentMemberRoleId(recipientRoleId);
        request.setPayType(fundMode.equals(FundModeEnum.PLATFORM_EXCHANGE.getCode()) ? 1 : 2);
        request.setPayMoney(payAmount.doubleValue());
        request.setOrderCode(orderNo);
        request.setRemark("");
        request.setPayPassword(payPassword);
        try {
            return assetAccountFeign.balancePay(request);
        } catch (BusinessException e) {
            return Wrapper.fail(e.getCode(), e.getMsg());
        }
    }

    /**
     * 余额支付退款
     * @param tradeNo      交易单号
     * @param refundAmount 退款金额
     * @param remark       备注
     * @return 退款结果
     */
    @Override
    public Wrapper<Void> balancePayRefund(String tradeNo, BigDecimal refundAmount, String remark) {
        BalanceRefundRequest request = new BalanceRefundRequest();
        request.setPayPlatformTradeCode(tradeNo);
        request.setRemark(remark);
        request.setTradeMoney(refundAmount.doubleValue());
        try {
            logger.info("余额退款参数 => " + OrderStringUtil.serializeObject(request));
            Wrapper<?> result = assetAccountFeign.balanceRefund(request);
            logger.info("余额退款返回 => code:" + result.getCode() + ", msg:" + result.getMessage());
            if(result.getCode() != ResponseCode.SUCCESS.getCode()) {
                return Wrapper.fail(result.getCode(), result.getMessage());
            }

            return Wrapper.success();
        } catch (BusinessException e) {
            logger.info("余额退款异常 => code:" + e.getCode() + ", msg:" + e.getMsg());
            return Wrapper.fail(e.getCode(), e.getMsg());
        } catch (Exception e) {
            logger.info("余额退款异常 => msg:" + e.getMessage());
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        }
    }

    /**
     * 通联支付
     *
     * @param loginSource     用户登录来源（用于标识Web、App支付）
     * @param orderPayChannel 支付渠道
     * @param buyerMemberId   采购会员Id
     * @param buyerRoleId     采购会员角色Id
     * @param fundMode        资金归集模式
     * @param payAmount       支付总金额
     * @param payAmounts      （如果是会员直接到账）收款方会员Id、角色Id、付款金额列表
     * @param tradeNo         付款交易号
     * @param attach          透传参数
     * @param weChatCode      小程序登录凭证
     * @return 支付结果
     */
    @Override
    public Wrapper<String> allInPay(Integer loginSource, OrderPayChannelEnum orderPayChannel, Long buyerMemberId, Long buyerRoleId, Integer fundMode, BigDecimal payAmount, List<PayAmountDTO> payAmounts, String tradeNo, String attach, String weChatCode) {
        EAccountOrderPayRequest request = new EAccountOrderPayRequest();
        request.setPayMemberId(buyerMemberId);
        request.setPayMemberRoleId(buyerRoleId);
        request.setOrderCode(tradeNo);
        request.setPayMoney(payAmount.doubleValue());

        if(payAmounts.size() == 1) {
            request.setMemberId(payAmounts.get(0).getVendorMemberId());
            request.setMemberRoleId(payAmounts.get(0).getVendorRoleId());
            request.setPayMoney(payAmounts.get(0).getPayAmount().doubleValue());
        } else {
            request.setReceiveList(payAmounts.stream().map(pay -> {
                ReceiveRequest receiveRequest = new ReceiveRequest();
                receiveRequest.setMemberId(pay.getVendorMemberId());
                receiveRequest.setMemberRoleId(pay.getVendorRoleId());
                receiveRequest.setPayMoney(pay.getPayAmount().doubleValue());
                return receiveRequest;
            }).collect(Collectors.toList()));
        }

        request.setServiceType(ServiceTypeEnum.Order_Pay.getCode());
        request.setRemark("");
        request.setAttach(attach);
        request.setPayType(fundMode.equals(FundModeEnum.PLATFORM_EXCHANGE.getCode()) ? 1 : 2);

        Integer payChannel = null;
        switch (orderPayChannel) {
            case ALLIN_WECHAT:
                if (StrUtil.isNotEmpty(weChatCode)) {
                    payChannel = EAccountPayChannelEnum.Applet_Wechat.getCode();
                } else if (loginSource.equals(UserLoginSourceEnum.BUSINESS_MOBILE.getCode())) {
                    payChannel = EAccountPayChannelEnum.APP_Wechat.getCode();
                } else {
                    payChannel = EAccountPayChannelEnum.Platform_Wechat.getCode();
                }
                break;
            case ALLIN_ALI_PAY:
                if (loginSource.equals(UserLoginSourceEnum.BUSINESS_MOBILE.getCode())) {
                    payChannel = EAccountPayChannelEnum.APP_Alipay.getCode();
                } else {
                    payChannel = EAccountPayChannelEnum.Platform_Alipay.getCode();
                }
                break;
            case ALLIN_BALANCE:
                payChannel = EAccountPayChannelEnum.Balance.getCode();
                break;
            case ALLIN_QUICK:
                payChannel = EAccountPayChannelEnum.Quick_Pay.getCode();
                break;
            case ALLIN_UNION:
                payChannel = EAccountPayChannelEnum.GateWay_Pay.getCode();
                break;
        }

        request.setPayChannel(payChannel);
        request.setJsCode(weChatCode);
        try {
            logger.info("通联支付参数 => " + OrderStringUtil.serializeObject(request));
            Wrapper<String> result = allInPayFeign.orderPay(request);
            logger.info("通联支付接口返回 => code:" + result.getCode() + ", msg:" + result.getMessage());
            return result;
        } catch (BusinessException e) {
            logger.info("通联支付返回异常 => code:" + e.getCode() + ", msg:" + e.getMsg());
            return Wrapper.fail(e.getCode(), e.getMsg());
        } catch (Exception e) {
            logger.info("通联支付异常 => msg:" + e.getMessage());
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        }
    }

    /**
     * 通联支付 退款
     *
     * @param buyerMemberId 采购会员Id（付款方会员Id）
     * @param buyerRoleId   采购会员角色Id（付款方会员角色Id）
     * @param fundMode          资金归集模式
     * @param payAmount         支付金额
     * @param tradeNo           （支付）交易号
     * @param refundNo          退款交易号
     * @return 退款结果
     */
    @Override
    public Wrapper<Void> allInPayRefund(Long buyerMemberId, Long buyerRoleId, Integer fundMode, BigDecimal payAmount, String tradeNo, String refundNo) {
        EAccountRefundRequest refundVO = new EAccountRefundRequest();
        refundVO.setMemberId(buyerMemberId);
        refundVO.setMemberRoleId(buyerRoleId);
        refundVO.setOrderCode(refundNo);
        refundVO.setPayCode(tradeNo);
        refundVO.setRefundMoney(payAmount.doubleValue());
        logger.info("通联支付退款参数 => " + OrderStringUtil.serializeObject(refundVO));
        try {
            Wrapper<?> result = allInPayFeign.orderRefund(refundVO);
            logger.info("通联支付退款结果 => code:" + result.getCode() + ", msg:" + result.getMessage());

            if(result.getCode() != ResponseCode.SUCCESS.getCode()) {
                return Wrapper.fail(result.getCode(), result.getMessage());
            }

            return Wrapper.success();
        } catch (BusinessException e) {
            logger.error("通联支付退款异常 => code:" + e.getCode() + ", msg:" + e.getMsg());
            return Wrapper.fail(e.getCode(), e.getMsg());
        } catch (Exception e) {
            logger.error("通联支付退款异常 => msg:" + e.getMessage());
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        }
    }

    /**
     * 建行支付
     *
     * @param buyerMemberName 采购会员名称
     * @param payAmount     支付总金额
     * @param tradeNo       付款交易号
     * @param attach        透传参数
     * @param merchantId    建行支付商户号
     * @param password      建行支付密码
     * @param branchId      建行支付分行代码
     * @param posId         建行支付商户柜台代码
     * @param publicKey     建行支付柜台公钥
     * @param timeoutDateTime   建行支付超时时间，格式为yyyyMMddHHmmss
     * @return 支付结果
     */
    @Override
    public Wrapper<String> ccbPay(String buyerMemberName, BigDecimal payAmount, String tradeNo, String attach, String merchantId, String password, String branchId, String posId, String publicKey, String timeoutDateTime) {
        CcbB2bPayVo payVo = new CcbB2bPayVo();
        payVo.setMerchantId(merchantId);
        payVo.setPwd(password);
        payVo.setBranchId(branchId);
        payVo.setPosId(posId);
        payVo.setPublicKey(publicKey);
        payVo.setOrderCode(tradeNo);
        payVo.setPayMent(payAmount);
        payVo.setAttach(attach);
        payVo.setRemark1(buyerMemberName);
        payVo.setRemark2("");
        payVo.setTimeout(timeoutDateTime);
        try {
            logger.info("建行B2B支付参数 => " + OrderStringUtil.serializeObject(payVo));
            Wrapper<String> result = ccbPayFeign.b2bPay(payVo);
            logger.info("建行B2B支付结果，code:" + result.getCode() + ", msg:" + result.getMessage() + ", data:" + result.getData());
            return result;
        } catch (Exception e) {
            logger.info("建行B2B支付异常， msg: " + e.getMessage());
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        }
    }

    /**
     * 建行B2B支付退款
     *
     * @param tradeNo   支付订单号
     * @param refundNo  退款订单号
     * @param payAmount 支付金额
     * @return 退款结果
     */
    @Override
    public Wrapper<Void> ccbPayRefund(String tradeNo, String refundNo, BigDecimal payAmount) {
        B2bRefundVO refundVO = new B2bRefundVO();
        refundVO.setOrderId(tradeNo);
        refundVO.setRefundCode(refundNo);
        refundVO.setMoney(payAmount.toPlainString());
        try {
            logger.info("建行B2B支付退款参数 => " + OrderStringUtil.serializeObject(refundVO));
            Wrapper<Boolean> refundResult = ccbPayFeign.b2bRefund(refundVO);
            logger.info("建行B2B支付退款结果，code:" + refundResult.getCode() + ", msg:" + refundResult.getMessage() + ", data:" + refundResult.getData());
            return (refundResult.getCode() == ResponseCode.SUCCESS.getCode() && refundResult.getData()) ? Wrapper.success() : Wrapper.fail(refundResult.getCode(), refundResult.getMessage());
        } catch (BusinessException e) {
            logger.error("建行B2B支付退款异常， code:" + e.getCode() + ", msg:" + e.getMsg());
            return Wrapper.fail(e.getCode(), e.getMsg());
        } catch (Exception e) {
            logger.error("建行B2B支付退款异常，msg:" + e.getMessage());
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        }
    }

    /**
     * 建行数字人民币支付
     *
     * @param loginSource       用户登录来源（用于标识Web、App支付）
     * @param buyerMemberName   采购会员名称
     * @param payAmount         支付总金额
     * @param tradeNo           付款交易号
     * @param attach            透传参数
     * @param merchantId        建行支付商户号
     * @param branchId          建行支付分行代码
     * @param posId             建行支付商户柜台代码
     * @param publicKey         建行支付柜台公钥
     * @param timeoutDateTime   建行支付超时时间，格式为yyyyMMddHHmmss
     * @param settlementAccount 商户结算账户
     * @param subMerchantId     二级商户号
     * @param returnUrl         退出支付流程时返回商户URL(H5支付才生效，二维码支付不生效)
     * @return 支付结果
     */
    @Override
    public Wrapper<String> ccbDigitalPay(Integer loginSource, String buyerMemberName, BigDecimal payAmount, String tradeNo, String attach, String merchantId, String branchId, String posId, String publicKey, String timeoutDateTime, String settlementAccount, String subMerchantId, String returnUrl) {
        CcbDigitalPayVo payVo = new CcbDigitalPayVo();
        payVo.setPayType(loginSource.equals(UserLoginSourceEnum.BUSINESS_WEB.getCode()) || loginSource.equals(UserLoginSourceEnum.BUSINESS_MANAGEMENT_PLATFORM.getCode()) ? 1 : 2);
        payVo.setMerchantId(merchantId);
        payVo.setPosId(posId);
        payVo.setBranchId(branchId);
        payVo.setPublicKey(publicKey);
        payVo.setOrderCode(tradeNo);
        payVo.setPayMent(payAmount);
        payVo.setAttach(attach);
        payVo.setRemark1(buyerMemberName);
        payVo.setRemark2("");
        payVo.setTimeout(timeoutDateTime);
        payVo.setSubMerchantId(subMerchantId);
        payVo.setCdtrWltId(settlementAccount);
        payVo.setMrchUrl(StringUtils.hasLength(returnUrl) ? returnUrl : "");
        try {
            logger.info("建行数字人民币支付参数 => " + OrderStringUtil.serializeObject(payVo));
            Wrapper<String> result = ccbPayFeign.digitalPay(payVo);
            logger.info("建行数字人民币支付结果，code:" + result.getCode() + ", msg:" + result.getMessage() + ", data:" + result.getData());
            return result;
        } catch (Exception e) {
            logger.info("建行数字人民币支付异常， msg: " + e.getMessage());
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        }
    }

    /**
     * 建行数字人民币支付退款
     *
     * @param orderCreateTime 订单创建时间
     * @param tradeNo         支付订单号
     * @param refundNo        退款订单号
     * @param payAmount       支付金额
     * @return 退款结果
     */
    @Override
    public Wrapper<Void> ccbDigitalPayRefund(LocalDateTime orderCreateTime, String tradeNo, String refundNo, BigDecimal payAmount) {
        DigitalRefundVO refundVO = new DigitalRefundVO();
        refundVO.setOrderCode(tradeNo);
        refundVO.setOrderStartTime(orderCreateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
        refundVO.setOrderEndTime(orderCreateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
        refundVO.setMoney(payAmount);
        refundVO.setRefundSn(refundNo);
        try {
            logger.info("建行数字人民币退款参数 => " + OrderStringUtil.serializeObject(refundVO));
            Wrapper<Boolean> refundResult = ccbPayFeign.digitalRefund(refundVO);
            logger.info("建行数字人民币支付退款结果，code:" + refundResult.getCode() + ", msg:" + refundResult.getMessage() + ", data:" + refundResult.getData());
            return (refundResult.getCode() == ResponseCode.SUCCESS.getCode() && refundResult.getData()) ? Wrapper.success() : Wrapper.fail(refundResult.getCode(), refundResult.getMessage());
        } catch (BusinessException e) {
            logger.error("建行数字人民币支付退款异常， code:" + e.getCode() + ", msg:" + e.getMsg());
            return Wrapper.fail(e.getCode(), e.getMsg());
        } catch (Exception e) {
            logger.error("建行数字人民币支付退款异常，msg:" + e.getMessage());
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        }
    }

    /**
     * 查询建行B2B支付订单
     *
     * @param tradeNo    付款交易号
     * @param merchantId 建行支付商户号
     * @param branchId   建行支付分行代码
     * @param posId      建行支付商户柜台代码
     * @param password   建行支付密码
     * @return 查询结果
     */
    @Override
    public Wrapper<OrderPaymentCallbackStatusEnum> queryCcbOrders(String tradeNo, String merchantId, String branchId, String posId, String password) {
        CcbB2bPayQueryVo queryVo = new CcbB2bPayQueryVo();
        queryVo.setOrderCode(tradeNo);
        queryVo.setMerchantId(merchantId);
        queryVo.setBranchId(branchId);
        queryVo.setPosId(posId);
        queryVo.setPassword(password);

        try {
            logger.info("查询建行B2B支付订单 => " + OrderStringUtil.serializeObject(queryVo));
            Wrapper<String> result = ccbPayFeign.b2bPayStatusQuery(queryVo);
            if(result.getCode() != ResponseCode.SUCCESS.getCode()) {
                logger.error("查询建行B2B支付订单返回错误，code:" + result.getCode() + ", msg:" + result.getMessage());
                //当订单不存在时，认为是“支付失败”
                return Wrapper.success(OrderPaymentCallbackStatusEnum.FAILED);
            }

            if(StringUtils.hasLength(result.getData())) {
                if(result.getData().equals(CcbB2bPayResultEnum.FAIL.getCode())) {
                    return Wrapper.success(OrderPaymentCallbackStatusEnum.FAILED);
                }

                if(result.getData().equals(CcbB2bPayResultEnum.SUCCESS.getCode())) {
                    return Wrapper.success(OrderPaymentCallbackStatusEnum.SUCCESS);
                }

                if(result.getData().equals(CcbB2bPayResultEnum.BANK_NOT_CONFIRM.getCode())) {
                    return Wrapper.success(OrderPaymentCallbackStatusEnum.PAYING);
                }

                //如果查询为“已退款”或“已经部分退款”，判定为“支付失败”
                return Wrapper.success(OrderPaymentCallbackStatusEnum.FAILED);
            }

            //如果查询返回Null，判定为“支付失败”
            return Wrapper.success(OrderPaymentCallbackStatusEnum.FAILED);
        } catch (Exception e) {
            logger.error("查询建行B2B支付订单异常, msg:" + e.getMessage());
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        }
    }

    /**
     * “会员支付参数”配置时，如果会员配置了授信额度支付，通知支付服务创建授信账户
     *
     * @param memberId 会员Id
     * @param roleId   会员角色Id
     */
    @Async
    @Override
    public void notifyCreditAsync(Long memberId, Long roleId) {
        try {
            CreditInitVO initVO = new CreditInitVO();
            initVO.setMemberId(memberId);
            initVO.setRoleId(roleId);
            Wrapper<?> creditResult = creditControllerFeign.init(initVO);
            if(creditResult.getCode() != ResponseCode.SUCCESS.getCode()) {
                logger.error("通知支付服务创建授信账户错误，code: " + creditResult.getCode() + ", message: " + creditResult.getMessage());
            }
        } catch (Exception e) {
            logger.error("通知支付服务创建授信账户错误：" + e.getMessage());
        }
    }

    /**
     * 查询会员授信是否存在
     *
     * @param buyerMemberId  采购会员Id
     * @param buyerRoleId    采购会员角色Id
     * @param vendorMemberId 供应会员Id
     * @param vendorRoleId   供应会员角色Id
     * @return true-存在，false-服务异常或不存在
     */
    @Override
    public Boolean findMemberCredit(Long buyerMemberId, Long buyerRoleId, Long vendorMemberId, Long vendorRoleId) {
        try {
            CreditFeignVO feignVO = new CreditFeignVO();
            feignVO.setMemberId(buyerMemberId);
            feignVO.setRoleId(buyerRoleId);
            feignVO.setParentMemberId(vendorMemberId);
            feignVO.setParentMemberRoleId(vendorRoleId);

            Wrapper<CreditFeignDetailVO> creditResult = creditControllerFeign.findCredit(feignVO);
            return creditResult.getCode() == ResponseCode.SUCCESS.getCode() && creditResult.getData() != null && creditResult.getData().getIsUsable().equals(EnableDisableStatus.ENABLE.getCode());
        } catch (Exception e) {
            logger.error("支付服务查询授信账户错误：" + e.getMessage());
            return false;
        }
    }

    /**
     * 授信支付
     *
     * @param buyerMemberId     采购会员Id
     * @param buyerRoleId       采购会员角色Id
     * @param recipientMemberId 收款方会员Id
     * @param recipientRoleId   收款方会员角色Id
     * @param fundMode          资金归集模式
     * @param orderNo           订单号
     * @param payAmount         支付金额
     * @param payPassword       Aes加密后的支付密码
     * @return 支付结果
     */
    @Override
    public Wrapper<String> creditPay(Long buyerMemberId, Long buyerRoleId, Long recipientMemberId, Long recipientRoleId, Integer fundMode, String orderNo, BigDecimal payAmount, String payPassword) {
        CreditPayVO payVO = new CreditPayVO();
        payVO.setMemberId(buyerMemberId);
        payVO.setRoleId(buyerRoleId);
        payVO.setParentMemberId(recipientMemberId);
        payVO.setParentMemberRoleId(recipientRoleId);
        payVO.setPayMoney(payAmount.doubleValue());
        payVO.setOrderCode(orderNo);
        payVO.setPayPassword(payPassword);
        payVO.setRemark("");
        Wrapper<CreditPayResponseVO> payResult = creditControllerFeign.pay(payVO);
        if(payResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return Wrapper.fail(payResult.getCode(), payResult.getMessage());
        }

        return Wrapper.success(payResult.getData().getPayCode());
    }

    /**
     * 授信支付退款
     *
     * @param buyerMemberId     采购会员Id
     * @param buyerRoleId       采购会员角色Id
     * @param recipientMemberId 收款方会员Id
     * @param recipientRoleId   收款方会员角色Id
     * @param tradeNo           支付单号
     * @param refundAmount      退款金额
     * @param reason            备注说明
     * @return 支付结果
     */
    @Override
    public Wrapper<String> creditPayRefund(Long buyerMemberId, Long buyerRoleId, Long recipientMemberId, Long recipientRoleId, String tradeNo, BigDecimal refundAmount, String reason) {
        CreditRefundVO refundVO = new CreditRefundVO();
        refundVO.setMemberId(buyerMemberId);
        refundVO.setMemberRoleId(buyerRoleId);
        refundVO.setParentMemberId(recipientMemberId);
        refundVO.setParentMemberRoleId(recipientRoleId);
        refundVO.setPayCode(tradeNo);
        refundVO.setRefundAmount(refundAmount.doubleValue());
        refundVO.setRemark(reason);
        return creditControllerFeign.refund(refundVO);
    }
}
